This section shows how you can obtain a pointer to a sound header stored in a sound resource. You can use this pointer to obtain information about the sound. You also need a pointer to a sound header to install a sampled sound as a voice in a channel (as described in "Installing Voices Into Channels" ) and to play sounds using low-level sound commands (as described below and in the next section). You can use a technique similar to the one described in this section if you wish to obtain a pointer to wave-table data that is stored in a sound resource.
Sound Manager versions 3.0 and later include the GetSoundHeaderOffset function that you can use to locate a sound header embedded in a sound resource. Listing 1-37 shows how to call the GetSoundHeaderOffset function and then pass the returned offset to the bufferCmd sound command, to play a sampled sound using low-level Sound Manager routines.
Listing 37 Playing a sound resource
FUNCTION MyPlaySampledSound (chan: SndChannelPtr; sndHandle: Handle): OSErr;
VAR
myOffset: LongInt;
mySndCmd: SndCommand; {a sound command}
myErr: OSErr;
BEGIN
myErr := GetSoundHeaderOffset(sndHandle, myOffset);
IF myErr = noErr THEN
BEGIN
HLock(sndHandle);
mySndCmd.cmd := bufferCmd; {command is bufferCmd}
mySndCmd.param1 := 0; {unused with bufferCmd}
mySndCmd.param2 := LongInt(ORD4(sndHandle^) + myOffset);
myErr := SndDoImmediate(chan, mySndCmd);
END;
MyPlaySampledSound := myErr;
END;
If the GetSoundHeaderOffset function is not available but you still need to obtain a pointer to a sound header, you can use the function MyGetSoundHeaderOffset defined in Listing 1-38 . The function defined there traverses a sound resource until it reaches the sound data. It returns, in the offset parameter, the offset in bytes from the beginning of a sound resource to the sound header.
The GetSoundHeaderOffset function is available in Sound Manager versions 3.0 and later. As a result, you'll need to use the techniques illustrated in Listing 1-38 only if you want your application to find a sound header when earlier versions of the Sound Manager are available.
Listing 38 Obtaining the offset in bytes to a sound header
FUNCTION MyGetSoundHeaderOffset (sndHdl: Handle; VAR offset: LongInt): OSErr;
TYPE
Snd1Header = {format 1 'snd ' resource header}
RECORD
format: Integer; {format of resource}
numSynths: Integer; {number of data types}
{synths, init option follow}
END;
Snd1HdrPtr = ^Snd1Header;
Snd2Header = {format 2 'snd ' resource header}
RECORD
format: Integer; {format of resource}
refCount: Integer; {for application use}
END;
Snd2HdrPtr = ^Snd2Header;
IntPtr = ^Integer; {for type coercion}
SndCmdPtr = ^SndCommand; {for type coercion}
VAR
myPtr: Ptr; {to navigate resource}
myOffset: LongInt; {offset into resource}
numSynths: Integer; {info about resource}
numCmds: Integer; {info about resource}
isDone: Boolean; {are we done yet?}
myErr: OSErr;
BEGIN
{Initialize variables.}
myOffset := 0; {return 0 if no sound header found}
myPtr := Ptr(sndHdl^); {point to start of resource data}
isDone := FALSE; {haven't yet found sound header}
myErr := noErr;
{Skip everything before sound commands.}
CASE Snd1HdrPtr(myPtr)^.format OF
firstSoundFormat: {format 1 'snd ' resource}
BEGIN {skip header start, synth ID, etc.}
numSynths := Snd1HdrPtr(myPtr)^.numSynths;
myPtr := Ptr(ORD4(myPtr) + SizeOf(Snd1Header));
myPtr := Ptr(ORD4(myPtr) +
numSynths * (SizeOf(Integer) + SizeOf(LongInt)));
END;
secondSoundFormat: {format 2 'snd ' resource}
myPtr := Ptr(ORD4(myPtr) + SizeOf(Snd2Header));
OTHERWISE {unrecognized resource format}
BEGIN
myErr := badFormat;
isDone := TRUE;
END;
END;
{Find number of commands and move to start of first command.}
numCmds := IntPtr(myPtr)^;
myPtr := Ptr(ORD4(myPtr) + SizeOf(Integer));
{Search for bufferCmd or soundCmd to obtain sound header.}
WHILE (numCmds >= 1) AND (NOT isDone) DO
BEGIN
IF (IntPtr(myPtr)^ = bufferCmd + dataOffsetFlag) OR
(IntPtr(myPtr)^ = soundCmd + dataOffsetFlag) THEN
BEGIN {bufferCmd or soundCmd found}
{copy offset from sound command}
myOffset := SndCmdPtr(myPtr)^.param2;
isDone := TRUE; {get out of loop}
END
ELSE
BEGIN {soundCmd or bufferCmd not found}
{move to next command}
myPtr := Ptr(ORD4(myPtr) + SizeOf(SndCommand));
numCmds := numCmds - 1;
END;
END; {WHILE}
offset := myOffset; {return offset}
MyGetSoundHeaderOffset := myErr; {return result code}
END;
The MyGetSoundHeaderOffset function defined in Listing 1-38 begins by initializing several variables, including a pointer that it sets to point to the beginning of the data contained in the sound resource. Then, after determining whether the sound resource is format 1 or format 2, the function skips data contained in the format 1 'snd ' resource header or in the format 2 'snd ' resource header, as appropriate.
Do not confuse the format 1 or format 2 'snd ' header with the sound header the MyGetSoundHeaderOffset function defined in Listing 1-38 is designed to find. A sound header contains information about the sampled-sound data stored in a sound resource; a sound resource header contains information about the format of the sound resource.
After skipping information in the sound resource header, MyGetSoundHeaderOffset simply looks through all sound commands in the resource for a bufferCmd or soundCmd command, either of which must contain the offset from the beginning of the resource to the sound header in its param2 field. If the given sound resource contains no sound header (and thus no sampled-sound data), the MyGetSoundHeaderOffset function returns an error and sets the offset variable parameter to 0.
After using the MyGetSoundHeaderOffset function to obtain an offset to the sound header, you can easily obtain a pointer to a sound header. Note, however, that because a handle to a sound resource is contained in a relocatable block, you must lock the relocatable block before you obtain a pointer to a sound header, and you must not unlock it until you are through using the pointer. Listing 1-39 demonstrates how you can convert an offset to a sound header into a pointer to a sound header after locking a relocatable block.
Listing 39 Converting an offset to a sound header into a pointer to a sound header
FUNCTION MyGetSoundHeader (sndHandle: Handle): SoundHeaderPtr;
VAR
myOffset: LongInt; {offset to sound header}
myErr: OSErr;
BEGIN
HLockHi(sndHandle); {lock data in high memory}
{compute offset to sound header}
myErr := MyGetSoundHeaderOffset(sndHandle, myOffset);
IF myErr <> noErr THEN
MyGetSoundHeader := NIL {no sound header in resource}
ELSE
{compute address of sound header}
MyGetSoundHeader := SoundHeaderPtr(ORD4(sndHandle^) + myOffset);
END;
The MyGetSoundHeader function defined in Listing 1-39 locks the sound handle you pass it in high memory and then attempts to find an offset to the sound header in the sound handle. If the MyGetSoundHeaderOffset function defined in Listing 1-38 returns an offset of 0, then MyGetSoundHeader returns a NIL pointer to a sound header; otherwise, it returns a pointer that remains valid as long as you do not unlock the sound handle.
The MyGetSoundHeader function returns a pointer to a sampled sound header even if the sound header is actually an extended sound header or a compressed sound header. Thus, before accessing any other fields of the sound header, you should test the encode field of the sound header to determine what type of sound header it is. Then, if the sound header is, for example, an extended sound header, cast the sampled sound header to an extended sound header. Then you can access any of the fields of the extended sound header. For an example of this technique, see Listing 1-26 .
| Previous | Chapter contents | Chapter top | Section top | Next |